﻿using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Dapper;
using BCCommon;
using BCEntity.Chats;

namespace BCData.Chats
{
    public class MessageData : IMessageData
    {
        private readonly IDatabaseContext databaseContext;

        public MessageData(IDatabaseContext databaseContext)
        {
            this.databaseContext=databaseContext;
        }

        public Task<IEnumerable<MessageEntity>> GetMessagesAsync(string userId, long? threadId, long? minMessageId, long? maxMessageId, int count)
        {
            const string sql = @"SELECT A.*
FROM chat_message A INNER JOIN chat_messagereceiver B ON A.MessageId = B.MessageId
WHERE {0}
ORDER BY A.MessageId DESC
LIMIT {1}";

            List<string> wheres = new List<string>();
            DynamicParameters parameters = new DynamicParameters();

            wheres.Add("B.Deleted = ?Deleted");
            parameters.Add("Deleted", false, DbType.Boolean);
            
            wheres.Add("A.Type <> ?ActionType");
            parameters.Add("ActionType", "Action", DbType.String);

            if (!string.IsNullOrWhiteSpace(userId))
            {
                wheres.Add("B.ReceiveUserId = ?ReceiveUserId");
                parameters.Add("ReceiveUserId", userId, DbType.String);
            }

            if (threadId != null)
            {
                wheres.Add("A.ThreadId = ?ThreadId");
                parameters.Add("ThreadId", threadId, DbType.Int64);
            }

            if (minMessageId != null)
            {
                wheres.Add("A.MessageId > ?MinMessageId");
                parameters.Add("MinMessageId", minMessageId, DbType.Int64);
            }

            if (maxMessageId != null)
            {
                wheres.Add("A.MessageId < ?MaxMessageId");
                parameters.Add("MaxMessageId", maxMessageId, DbType.Int64);
            }

            return this.databaseContext.Slave.QueryAsync<MessageEntity>(string.Format(sql, string.Join(" AND ", wheres), count), parameters);
        }

        public Task<MessageEntity> GetMessageAsync(long messageId)
        {
            var sql = typeof(MessageEntity).GetSelectSqlByPrimaryKeys();

            return this.databaseContext.Slave.QueryFirstOrDefaultAsync<MessageEntity>(sql, new { messageId });
        }

        public Task<MessageEntity> InsertAsync(MessageEntity entity)
        {
            return this.databaseContext.Master.InsertAsync(entity, this.databaseContext.Transaction);
        }

        public Task<MessageEntity> UpdateAsync(MessageEntity entity)
        {
            return this.databaseContext.Master.UpdateAsync(entity, this.databaseContext.Transaction);
        }

        public async Task<IDictionary<long, int>> GetUnreadMessageCountAsync(string userId, IEnumerable<long> threadIds)
        {
            if (threadIds == null)
            {
                const string sql = @"SELECT A.ThreadId, COUNT(1) FROM chat_message A INNER JOIN chat_messagereceiver B ON A.MessageId = B.MessageId AND A.CreateUserId <> B.ReceiveUserId
WHERE A.Type <> 'Info' AND A.Type <> 'Action' AND B.ReadTime IS NULL AND B.ReceiveUserId = ?ReceiveUserId
GROUP BY A.ThreadId";

                var results = await this.databaseContext.Slave.QueryAsync<(long, int)>(sql, new { ReceiveUserId = userId });

                return results.ToDictionary(p => p.Item1, p => p.Item2);
            }

            if (!threadIds.Any())
            {
                return new Dictionary<long, int>();
            }
            else
            {
                const string sql = @"SELECT A.ThreadId, COUNT(1) FROM chat_message A INNER JOIN chat_messagereceiver B ON A.MessageId = B.MessageId AND A.CreateUserId <> B.ReceiveUserId
WHERE A.Type <> 'Info' AND A.Type <> 'Action' AND B.ReadTime IS NULL AND B.ReceiveUserId = ?ReceiveUserId AND A.ThreadId IN @ThreadIds
GROUP BY A.ThreadId";

                var results = await this.databaseContext.Slave.QueryAsync<(long, int)>(sql, new { ReceiveUserId = userId, threadIds });

                return results.ToDictionary(p => p.Item1, p => p.Item2);
            }
        }
    }
}
